package com.vhall.component.framework.common.utils;

import com.vhall.component.framework.common.exception.BaseException;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.framework.common.model.DebugThrowable;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 异常处理类
 *
 * @author xiaoliang.wang@vhall.com
 */
public class Throwabler {

    /**
     * 获取第一条异常信息
     *
     * @param throwable
     * @return
     */
    public static String getFirstStackInfo(Throwable throwable) {
        StackTraceElement[] stackTrace = throwable.getStackTrace();
        if (stackTrace != null && stackTrace.length > 0) {
            return stackTrace[0].toString();
        }
        return null;
    }

    /**
     * 获取详细异常信息
     *
     * @param e
     * @return
     */
    public static Throwable getCause(Exception e) {
        Throwable cause = e.getCause();
        if (cause != null) {
            while (cause.getCause() != null) {
                cause = cause.getCause();
            }
        }
        return Objects.nonNull(cause) ? cause : e;
    }

    /**
     * 获取详细异常信息
     *
     * @param e
     * @return
     */
    public static Throwable getCause(Throwable e) {
        Throwable cause = e.getCause();
        if (cause != null) {
            while (cause.getCause() != null) {
                cause = cause.getCause();
            }
        }
        return Objects.nonNull(cause) ? cause : e;
    }

    /**
     * 按异常类型生成对应的 debugInfo
     *
     * @param exception
     * @return
     */
    public static DebugThrowable debugSystemcThrowable(Throwable e) {
        Throwable cause = getCause(e);
        // 业务异常，只包装 message
        if (cause instanceof BaseException) {
            return new DebugThrowable(cause, null);
        }
        if (cause instanceof BusinessException) {
            return new DebugThrowable(cause, null);
        }
        // 其它未知异常
        return new DebugThrowable(cause, buildConsoleStack(cause));
    }

    /**
     * 构造堆栈信息
     *
     * @param cause
     * @return
     */
    public static String buildConsoleStack(Throwable e) {
        StringBuilder build = new StringBuilder(512);
        StackTraceElement[] traces = e.getStackTrace();
        build.append("[error] > ").append(e.getClass().getName()).append(": ").append(e.getMessage()).append("\n");
        for (int i = 0, len = traces.length; i < len; i++) {
            if (i == 10) {
                break;
            }
            StackTraceElement trace = traces[i];
            if (Objects.nonNull(trace)) {
                build.append("		    [error] > ");
                build.append(trace.getClassName()).append(".").append(trace.getMethodName());
                build.append("at").append("(").append(trace.getFileName()).append(":").append(trace.getLineNumber()).append(")").append("\n");
            }
        }
        return build.deleteCharAt(build.length() - 1).toString();
    }

    /**
     * 输出异常 message
     *
     * @param throwable
     * @return
     */
    public static Object message(Throwable throwable) {
        Object original = null;
        if (throwable instanceof BaseException) {
            original = ((BaseException) throwable).getOriginal();
            if (Objects.isNull(original)) {
                original = throwable.getMessage();
            }
        } else {
            original = throwable.getMessage();
        }
        return original;
    }

    /**
     * 输出堆栈信息
     *
     * @param throwable
     * @return
     */
    public static Object buildstack(Throwable throwable) {
        StackTraceElement[] stackTrace = throwable.getStackTrace();
        if (Objects.nonNull(stackTrace)) {
            List<String> stackInfo = new ArrayList<>();
            String message = throwable.getMessage();
            stackInfo.add(throwable.getClass().toString().concat(" :").concat(Objects.nonNull(message) ? message : ""));
            for (StackTraceElement stack : stackTrace) {
                if (stack.getClassName().startsWith("com.vhall")) {
                    stackInfo.add(stack.toString());
                }
            }
            return stackInfo;
        }
        return null;
    }

}
